An API-key is revoked by permanently deleting its API_KEY subject:
DELETE physically removes the subject together with its
grants and its stored API-key hash, so the key immediately stops
authenticating. There is no soft-delete for API_KEY subjects. As a
safeguard against deleting the wrong subject, the request has to repeat
the subject’s name and type as query parameters, which are verified
against the subject identified by the UUID in the path.
| name | value |
|---|---|
| subjectUuid | a91c000b-0000-0000-0000-00000000000b |
| subjectName | temporary.key |
API_KEY subjects authenticate technical clients via the
Hostsharing-Api-Key HTTP header instead of a Keycloak OIDC
JWT, e.g. automation programs, completely bypassing Keycloak. Only a
global-admin may create API_KEY subjects. The clear-text API-key is
returned only once, in the response of creating the API_KEY subject;
just its hash gets stored. Like GROUP subjects, API_KEY subjects cannot
have an account. Global API_KEY subjects do not belong to a realm, thus
their name must neither contain a - (the realm-prefix
delimiter) nor a / (the GROUP subject marker).
The response contains the generated clear-text API-key (property
apiKey) exactly once; it cannot be retrieved again.
HTTP POST "/api/rbac/subjects" \
-H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
`# {` \
`# "sub" : "uuid<hsh-alex_superuser>"` \
`# }` \
<<EOF
{
"uuid" : "a91c000b-0000-0000-0000-00000000000b",
"name" : "temporary.key",
"type" : "API_KEY"
}
EOF
=> status: 201 CREATED a91c000b-0000-0000-0000-00000000000b
{
"uuid" : "a91c000b-0000-0000-0000-00000000000b",
"name" : "temporary.key",
"organization" : "temporary",
"type" : "API_KEY",
"apiKey" : "hsak_temporary.key.b60265f64e9e6a43c9a0cc2f0b33a8a6f1455ea7e4ea04caec8dac77112791eb",
"scopes" : null,
"expiresAt" : null
}
The grant API needs the UUID of the role which we want to grant.
HTTP GET "/api/rbac/roles?name=rbac.global%23global%3AADMIN" \
-H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
`# {` \
`# "sub" : "uuid<hsh-alex_superuser>"` \
`# }`
=> status: 200 OK
[ {
"uuid" : "2366934b-3ba5-453a-a070-78698db295e0", // globalAdminRoleUuidToGrant
"object.uuid" : "a8842cb7-7284-468a-9e78-07e6fea8bc98",
"objectTable" : "rbac.global",
"objectIdName" : "global",
"roleType" : "ADMIN",
"roleName" : "rbac.global#a8842cb7-7284-468a-9e78-07e6fea8bc98:ADMIN",
"roleIdName" : "rbac.global#global:ADMIN"
} ]
HTTP POST "/api/rbac/grants" \
-H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
`# {` \
`# "sub" : "uuid<hsh-alex_superuser>"` \
`# }` \
-H 'Hostsharing-Assumed-Roles: rbac.global#global:ADMIN' \
<<EOF
{
"assumed" : true,
"grantedRole.uuid" : "2366934b-3ba5-453a-a070-78698db295e0", // globalAdminRoleUuidToGrant
"granteeSubject.uuid" : "a91c000b-0000-0000-0000-00000000000b"
}
EOF
=> status: 201 CREATED 2366934b-3ba5-453a-a070-78698db295e0 // globalAdminRoleUuidToGrant
HTTP GET "/api/hs/accounts/current" \
-H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK
{
"subject" : {
"uuid" : "a91c000b-0000-0000-0000-00000000000b",
"name" : "temporary.key",
"organization" : "temporary",
"type" : "API_KEY"
},
"person" : null,
"globalAdmin" : true
}
HTTP GET "/api/rbac/context" \
-H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK
{
"subject" : {
"uuid" : "a91c000b-0000-0000-0000-00000000000b",
"name" : "temporary.key",
"organization" : null,
"type" : "API_KEY"
},
"assumedRoles" : [ ],
"claimedGroups" : [ ],
"effectiveGroups" : [ ],
"globalAdmin" : true,
"apiKey" : {
"scopes" : [ ],
"expiresAt" : null
}
}
HTTP DELETE "/api/rbac/subjects/a91c000b-0000-0000-0000-00000000000b?name=temporary.key&type=API_KEY" \
-H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
`# {` \
`# "sub" : "uuid<hsh-alex_superuser>"` \
`# }`
=> status: 204 NO_CONTENT
HTTP GET "/api/rbac/context" \
-H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 401 UNAUTHORIZED
{
"path" : "/api/rbac/context",
"statusCode" : 401,
"statusPhrase" : "Unauthorized",
"message" : "ERROR: [401] invalid API-key"
}
generated on 2026-08-10 04:34:38 for branch HEAD